/**
 * 
 */
package com.m7012e.a_remote.ar_listeners.old.network;

import java.io.IOException;
import java.net.UnknownHostException;

import com.leoj.utils.CallbackMethod;
import com.leoj.utils.Couple;
import com.leoj.utils.Handler;
import com.leoj.utils.finite_states_machine.Machine;
import com.m7012e.a_remote.ar_listeners.exception.WrongMessageProtocolException;
import com.m7012e.a_remote.ar_listeners.old.network.protocols.DaemonMulticastState;
import com.m7012e.a_remote.ar_listeners.old.network.protocols.DaemonTcpState;
import com.m7012e.a_remote.commons.ARemoteTcpConnection;
import com.m7012e.a_remote.commons.ARemoteTcpSender;
import com.m7012e.a_remote.communication.DisplayDiscoveryMessage;
import com.m7012e.a_remote.communication.Message;
import com.m7012e.a_remote.communication.MulticastMessage;
import com.m7012e.a_remote.communication.TcpMessage;
import com.m7012e.a_remote.communication.tcpMessages.DisplayRequestMessage;
import com.m7012e.a_remote.communication.tcpMessages.EvaluatedDistanceMessage;
import com.m7012e.a_remote.communication.tcpMessages.NotAvailableMessage;
import com.m7012e.a_remote.communication.tcpMessages.OkMessage;

/**
 * @author leoj
 * @param <DaemonTcpState>
 *
 */
public class ARemoteDaemonMessageAnalyzer implements Handler<Message>{
	
	private Machine<DaemonMulticastState, MulticastMessage> _multicastProtocol;
	private MulticastMessage _lastMulticastedMsg;
	private Machine<DaemonTcpState, TcpMessage> _tcpProtocol;
	private TcpMessage _lastTcpMsg;
	private ARemoteTcpConnection _tcpConnection;
	/**
	 * 
	 */
	public ARemoteDaemonMessageAnalyzer() {
		
		Handler<DaemonMulticastState> actions = new Handler<DaemonMulticastState>() {
			@Override
			public void handle(DaemonMulticastState st) {
				switch(st){
				case WAITING:
					//do nothing ... 
					// in a more advance version : maybe something smarter to do here ...  
					break;
				case HANDLING_DISCOVERY:
					if (_lastMulticastedMsg instanceof DisplayDiscoveryMessage) {
						DisplayDiscoveryMessage remote = (DisplayDiscoveryMessage) _lastMulticastedMsg;
						if(_tcpProtocol.getCurrentState() != DaemonTcpState.WAITING){
							NotAvailableMessage m = new NotAvailableMessage(0);
							try {
								ARemoteTcpSender.sendMessage(m,remote.remoteAddress, remote.remotePort);
							} catch (UnknownHostException e) {
								e.printStackTrace();
							} catch (IOException e) {
								e.printStackTrace();
							}
							//TODO See if we really need to send a NotAvailable Message ...
							// Maybe we can jsut ignore it ... 
						}else{
							try {
								//TODO see what can create this EvaluatedDistanceMessage ! 
								EvaluatedDistanceMessage answer = new EvaluatedDistanceMessage(_tcpConnection.getAddress(), _tcpConnection.getPort(), 0);
								ARemoteTcpSender.sendMessage(answer, remote.remoteAddress, remote.remotePort);
							} catch (UnknownHostException e) {
								e.printStackTrace();
							} catch (IOException e) {
								e.printStackTrace();
							}
						}
					}else{
						throw new WrongMessageProtocolException();
					}
					_multicastProtocol.handle(null);
					break;
				default:
					break;
				}
			}
		};
		CallbackMethod<DaemonMulticastState, Couple<DaemonMulticastState, MulticastMessage>> transition =
			new CallbackMethod<DaemonMulticastState, Couple<DaemonMulticastState,MulticastMessage>>() {
				@Override
				public DaemonMulticastState handle(Couple<DaemonMulticastState, MulticastMessage> args) {
					MulticastMessage msg = args.getSecond();
					DaemonMulticastState state = args.getFirst();
					switch (state) {
					case WAITING:
						if (msg instanceof DisplayDiscoveryMessage) {
							return DaemonMulticastState.HANDLING_DISCOVERY;
						}else{
							return state;
						}
					case HANDLING_DISCOVERY:
						//unconditional transition !
						return DaemonMulticastState.WAITING;
					default:
						throw new WrongMessageProtocolException();
					}
				}
			};
		_multicastProtocol = new Machine<DaemonMulticastState, MulticastMessage>(DaemonMulticastState.WAITING, actions, transition);
		
		CallbackMethod<DaemonTcpState, Couple<DaemonTcpState, TcpMessage>> tcpTransitions = 
			new CallbackMethod<DaemonTcpState, Couple<DaemonTcpState,TcpMessage>>() {
				@Override
				public DaemonTcpState handle(Couple<DaemonTcpState, TcpMessage> args) {
					DaemonTcpState currentState = args.getFirst();
					TcpMessage incomingMsg = args.getSecond();
					switch (currentState) {
					case WAITING:
						if(incomingMsg instanceof DisplayRequestMessage){
							try {
								_tcpConnection.sendMessage(new OkMessage());
								return DaemonTcpState.DISPLAYING;
							} catch (IOException e) {
								e.printStackTrace();
							}
							return DaemonTcpState.WAITING;
						}else{
							//we don't care .. keep on waiting !
						return DaemonTcpState.WAITING;
						}
					case DISPLAYING:
						if(incomingMsg instanceof DisplayRequestMessage){
							try {
								_tcpConnection.sendMessage(new NotAvailableMessage(0));
							} catch (IOException e) {
								e.printStackTrace();
							}
							return DaemonTcpState.DISPLAYING;
						}
						return null;
					default:
						throw new WrongMessageProtocolException();
					}
				}
			};
		_tcpProtocol = new Machine<DaemonTcpState, TcpMessage>(DaemonTcpState.WAITING, tcpTransitions);
	}
	
	public void setTcpConnection(ARemoteTcpConnection connection){
		_tcpConnection = connection;
	}
	
	public void handle(MulticastMessage m){
		_lastMulticastedMsg = m;
		_multicastProtocol.handle(_lastMulticastedMsg);
	}
	public void handle(TcpMessage m){
		_lastTcpMsg = m;
		_tcpProtocol.handle(_lastTcpMsg);
	}
	public void handle(Message m){
		throw new RuntimeException("this message is not compatible with the Tcp or Multicast type !");
	}
}
